package ldh.im.rpc.buffer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class RpcComposeBuffer implements RpcBuffer {

    private RpcBuffer[] rpcBuffers;
    private int arrayStart = 0;
    private int arrayEnd = 0;
    private int markReadArray = 0;
    private int markWriteArray = 0;
    private int itemSize;

    public RpcComposeBuffer(int itemSize, int length) {
        rpcBuffers = new RpcBuffer[length];
        this.itemSize = itemSize;
        rpcBuffers[0] = new RpcByteBuffer(itemSize);
        arrayStart = 0;
        arrayEnd = 0;
    }

    @Override
    public int getReadIdx() {
        return rpcBuffers[arrayStart].getReadIdx();
    }

    @Override
    public int getWriteIdx() {
        return rpcBuffers[arrayEnd].getWriteIdx();
    }

    @Override
    public int readable() {
        int totalReadable = 0;
        if (arrayStart == arrayEnd) {
            totalReadable = rpcBuffers[arrayStart].readable();
        } else {
            for (int i = arrayStart; i<arrayEnd; i++) {
                totalReadable += rpcBuffers[i].readable();
            }
        }
        return totalReadable;
    }

    @Override
    public int writeable() {
        int totalWriteable = 0;
        if (arrayStart == arrayEnd) {
            totalWriteable = rpcBuffers[arrayStart].readable();
        } else {
            for (int i = arrayStart; i<arrayEnd; i++) {
                totalWriteable += rpcBuffers[i].readable();
            }
        }
        return totalWriteable;
    }

    @Override
    public void writeBytes(byte[] bytes) {
        writeBytes(bytes, 0, bytes.length);
    }

    @Override
    public void writeBytes(byte[] bytes, int start, int length) {
        RpcBuffer currentBuffer = rpcBuffers[arrayEnd];
        int currentBufferWriteable = currentBuffer.writeable();
        if (currentBufferWriteable > bytes.length) {
            currentBuffer.writeBytes(bytes);
        } else {
            currentBuffer.writeBytes(bytes, 0, currentBufferWriteable);
            ensureSize(bytes.length-currentBufferWriteable);
            writeBytes(bytes, start+currentBufferWriteable, length-currentBufferWriteable);
        }
    }

    @Override
    public void writeShort(short value) {

    }

    @Override
    public void writeInt(int value) {

    }

    @Override
    public void writeByte(byte bb) {
        ensureSize(1);
        RpcBuffer currentBuffer = rpcBuffers[arrayEnd];
        currentBuffer.writeByte(bb);
    }

    @Override
    public void readBytes(byte[] bytes) {

    }

    @Override
    public short readShort() {
        return 0;
    }

    @Override
    public int readInt() {
        return 0;
    }

    @Override
    public void discard() {
        if (arrayStart == 0) {
            if (arrayStart == arrayEnd) {
                RpcBuffer buffer = rpcBuffers[arrayStart];
                buffer.discard();
            }
            for (int i=arrayEnd+1; i<rpcBuffers.length; i++) {
                rpcBuffers[i] = null;
            }
        } else {
            for (int i=arrayStart; i<=arrayEnd; i++) {
                rpcBuffers[i-arrayStart]=rpcBuffers[i];
            }
            arrayEnd = arrayEnd - arrayStart;
            arrayStart = 0;
        }
    }

    @Override
    public void markReadIdx() {
        this.markReadArray = this.arrayStart;
        RpcBuffer currentBuffer = rpcBuffers[arrayStart];
        currentBuffer.markReadIdx();
    }

    @Override
    public void resetMarkReadIdx() {
        for (int i=this.markReadArray; i<=arrayStart; i++) {
            RpcBuffer currentBuffer = rpcBuffers[i];
            currentBuffer.resetMarkReadIdx();
        }
    }

    @Override
    public void markWriteIdx() {
        this.markWriteArray = this.arrayEnd;
        RpcBuffer currentBuffer = rpcBuffers[arrayEnd];
        currentBuffer.markWriteIdx();
    }

    @Override
    public void resetMarkWriteIdx() {
        for (int i=this.markWriteArray; i<=arrayEnd; i++) {
            RpcBuffer currentBuffer = rpcBuffers[i];
            currentBuffer.resetMarkReadIdx();
        }
    }

    @Override
    public void writeToStream(OutputStream outputStream) throws IOException {
        for (int i=this.arrayStart; i<=arrayEnd; i++) {
            RpcBuffer buffer = rpcBuffers[i];
            buffer.writeToStream(outputStream);
        }
    }

    @Override
    public int readFromStream(InputStream inputStream, int length) throws IOException {
        RpcBuffer buffer = rpcBuffers[arrayEnd];
        int writeSize = buffer.writeable();
        int readSize = -1;
        if (writeSize >= length) {
            readSize = buffer.readFromStream(inputStream, length);
            if (readSize < 1) return readSize;
        } else {
            ensureSize(length-writeSize);
            int readSize1 = readFromStream(inputStream, length-writeSize);
            if (readSize1 < 1) {
                return readSize;
            } else {
                readSize += readSize1;
            }
        }
        return readSize;
    }

    @Override
    public void ensureSize(int size) {
        RpcBuffer buffer = rpcBuffers[arrayEnd];
        int writeSize = buffer.writeable();
        if (writeSize < size) {
            rpcBuffers[arrayEnd+1] = new RpcByteBuffer(itemSize);
            arrayEnd += 1;
        }
    }

    public byte[] array() {
        return new byte[0];
    }

    public void skip(int length) {
        // todo
    }
}
